package routers

import (
	"container/list"
	"fmt"
	"gitee.com/stevenchin/gin_study/async"
	"gitee.com/stevenchin/gin_study/htmltemplate"
	"gitee.com/stevenchin/gin_study/middleware"
	"gitee.com/stevenchin/gin_study/middleware/exceptiontest"
	"gitee.com/stevenchin/gin_study/paramcalibration"
	"gitee.com/stevenchin/gin_study/request"
	"gitee.com/stevenchin/gin_study/response"
	"gitee.com/stevenchin/gin_study/xorm"
	"github.com/gin-gonic/gin"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"
)

type JsonStruct struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
	Addr string `json:"address"`
}

const (
	LINUX = "linux"
)

func SetRouters() *gin.Engine {
	engine := gin.Default()
	// 注册全局中间件。这里是有两个中间件：一个自定义的全局异常处理中间件；另一个观察执行顺序中间件
	// 	TIPS：GlobalErrorHandler()要尽量放在第一个被加载。如不是的话，在recover()前的中间件或路由，将不能被拦截到
	//	程序的原理是：
	//		1.请求进来，执行 recover
	//		2.程序异常，抛出 panic
	//		3.panic 被 recover 捕获，返回异常信息并 Abort,终止这次请求
	engine.Use(middleware.GlobalErrorHandler(), middleware.Global())
	// 指定html模板文件位置（根据操作系统指定不同位置）
	sysType := runtime.GOOS
	if sysType == LINUX {
		engine.LoadHTMLGlob("/opt/go/template/**")
		// 如果需要引入静态文件需要定义一个静态文件目录
		// engine.Static("/assets", ""/opt/go/assets")
	} else {
		engine.LoadHTMLGlob("htmltemplate/template/**")
		// 如果需要引入静态文件需要定义一个静态文件目录
		// engine.Static("/assets", "./assets")
	}

	// 1. 基本的路由注册之绑定路由规则和执行的函数。gin.Context封装了request和response
	engine.GET("/", func(context *gin.Context) {
		context.String(http.StatusOK, "gin get")
	})
	// 2. 执行函数为非匿名函数
	engine.GET("/router", helloHandler)
	// 3. Restful风格的API(GET/POST/PUT/DELETE ......)
	engine.POST("/", func(context *gin.Context) {
		context.String(http.StatusOK, "gin post")
	})
	engine.PUT("/", func(context *gin.Context) {
		context.String(http.StatusOK, "gin put")
	})
	engine.DELETE("/", func(context *gin.Context) {
		context.String(http.StatusOK, "gin delete")
	})
	// 4. 可以通过Context的Param方法来获取【URL路径值】
	engine.GET("/api/:name/*action", func(context *gin.Context) {
		name := context.Param("name")
		s := context.Param("action")
		//截取
		action := strings.Trim(s, "/")
		//访问：http://localhost:8000/api/steven/qinxh.com/
		//输出结果为：steven is qinxh.com
		context.String(http.StatusOK, name+" is "+action)
	})
	// 5. 通过DefaultQuery()或Query()方法获取【URL参数值】。DefaultQuery()若参数不存在返回默认值，Query()若不存在返回空串
	engine.POST("/api", func(context *gin.Context) {
		//指定默认值
		age := context.Query("age")
		name := context.DefaultQuery("name", "steven")
		addr := context.DefaultQuery("addr", "addr is my addr")
		//访问：http://localhost:8000/api?name=sss&age=111
		//输出结果为：name is sss, age is 111
		context.String(http.StatusOK, fmt.Sprintf("name is %s, age is %s, addr is %s", name, age, addr))
	})
	// 6. 获取【表单参数值】。表单参数可以通过 PostForm() 方法获取，该方法默认解析的是 x-www-form-urlencoded 或 form-data 格式
	//    的参数表单传输为post请求，http常见的传输格式为四种：
	// 		· application/json；
	// 		· application/x-www-form-urlencoded；
	// 		· application/xml；
	// 		· multipart/form-data

	// 6.1 x-www-form-urlencoded或form-data格式的数据
	engine.POST("/form", func(context *gin.Context) {
		types := context.DefaultPostForm("type", "post")
		username := context.PostForm("username")
		password := context.PostForm("password")
		context.String(http.StatusOK, fmt.Sprintf("username:%s,password:%s,type:%s", username, password, types))
	})
	// 6.2 application/json
	engine.POST("/form/json", func(context *gin.Context) {
		jsonStruct := JsonStruct{}
		if err := context.BindJSON(&jsonStruct); err == nil {
			name := jsonStruct.Name
			age := jsonStruct.Age
			context.JSON(http.StatusOK, gin.H{"code": 000, "msg": "success", "data": fmt.Sprintf("name is %s, age is %d", name, age)})
		} else {
			context.JSON(http.StatusInternalServerError, gin.H{"code": 400, "msg": err.Error()})
		}
	})
	// 6.3 multipart/form-data，此格式用于文件上传。gin文件上传与原生的net/http方法类似，不同在于gin把原生的request封装到c.Request中
	//总体上限制上传最大尺寸（如果需要个性化校验，最好在代码块中单独校验）
	engine.MaxMultipartMemory = 8 << 20
	engine.POST("/form/file", func(context *gin.Context) {
		if file, errs := context.FormFile("file"); errs != nil {
			context.String(http.StatusInternalServerError, "上传图片出错"+errs.Error())
		} else {
			split := strings.Split(file.Filename, ".")
			s := split[len(split)-1]
			if s != "png" {
				context.String(http.StatusBadRequest, "只允许上传png图片，此文件格式为："+s)
				return
			}
			// 保存在当前目录下
			if err := context.SaveUploadedFile(file, file.Filename); err != nil {
				context.String(http.StatusInternalServerError, "保存图片出错"+err.Error())
				return
			}
			// 删除文件（TIPS：可以打断点看看文件所在位置）
			defer func(name string) {
				err := os.Remove(name)
				if err != nil {

				}
			}(file.Filename)
			context.String(http.StatusOK, fmt.Sprintf("保存图片：%s 成功", file.Filename))
		}
	})
	//上传多个文件
	engine.POST("/form/multiFile", func(context *gin.Context) {
		if form, err := context.MultipartForm(); err != nil {
			context.String(http.StatusInternalServerError, "上传图片出错"+err.Error())
		} else {
			// 获取所有图片
			files := form.File["files"]
			fileNames := list.New()
			// 遍历所有图片
			for _, file := range files {
				// 逐个存
				if err := context.SaveUploadedFile(file, file.Filename); err != nil {
					context.String(http.StatusBadRequest, fmt.Sprintf("upload err %s", err.Error()))
					return
				} else {
					fileNames.PushBack(file.Filename)
				}
			}
			// 逐个删
			defer func(l list.List) {
				for front := l.Front(); front != nil; front = front.Next() {
					fmt.Println(fmt.Sprintf("deleted fileName is %s", front.Value.(string)))
					err := os.Remove(front.Value.(string))
					if err != nil {

					}
				}
			}(*fileNames)
			context.String(http.StatusOK, fmt.Sprintf("upload ok %d files", len(files)))
		}
	})
	// 7. routes group。管理一些相同前缀的URL
	group1 := engine.Group("/group1")
	// {} 是书写规范（非必须这样书写）
	{
		group1.GET("/login", login)
		group1.GET("/submit", submit)
	}
	group2 := engine.Group("/group2")
	{
		group2.POST("/login", login)
		group2.POST("/submit", submit)
	}

	// 数据解析与结构体绑定
	requestGroup := engine.Group("/data/request")
	{
		// 8. Json 数据解析和绑定
		requestGroup.POST("/json", request.JsonTest)
		// 9. 表单数据解析和绑定
		requestGroup.POST("/form", request.FormTest)
		// 10. URI数据解析和绑定（需要和结构体中别名对应上，否则会报错）
		requestGroup.POST("/uri/:user/:password", request.UriTest)
		requestGroup.POST("/xml", request.XmlTest)
		// 11. Json + URI 数据解析和绑定
		requestGroup.POST("/jsonAndUri/:user/:password", request.JsonAndUri)
	}
	// 数据响应
	responseGroup := engine.Group("/data/response")
	{
		// 12. Json数据格式的响应
		responseGroup.POST("/json", response.JsonResponse)
		// 13. 结构体数据格式的响应
		responseGroup.POST("/struct", response.StructResponse)
		// 14. XML数据格式的响应
		responseGroup.POST("/xml", response.XmlResponse)
		// 15. YAML数据格式的响应
		responseGroup.POST("/yaml", response.YamlResponse)
		// 16. ProtoBuf数据格式的响应
		responseGroup.POST("/proBuf", response.ProtoBufResponse)
		// 17. HTML模板
		responseGroup.GET("/html", htmltemplate.HtmlRendering)
	}
	// 18. 访问页面重定向
	redirectGroup := engine.Group("/data/redirect")
	{
		redirectGroup.GET("", htmltemplate.Redirect)
		redirectGroup.GET("/k", htmltemplate.Redirect1)
		redirectGroup.GET("/teacher", htmltemplate.Redirect2)
		redirectGroup.GET("/center", htmltemplate.Redirect3)
	}

	processingGroup := engine.Group("/data/processing")
	{
		// 19. 异步处理
		processingGroup.GET("/async", async.Async)
		// 20. 同步处理
		processingGroup.GET("/sync", async.Sync)
		// 实现 java 中 CompletableFuture 异步处理效果
		processingGroup.GET("/future1", async.FutureSupplyAsync)
		processingGroup.GET("/future2", async.FutureAllOf)
		processingGroup.GET("/future3", async.FutureSupplyThenApply)
	}
	middlewareGroup := engine.Group("/data/middleware")
	{
		// 21. 全局中间件验证
		middlewareGroup.GET("/global", func(context *gin.Context) {
			// TIPS：为了便于观察全局中间件的 context.Next()使用，这里睡眠1秒钟
			time.Sleep(1 * time.Second)
			// 取值
			if value, exists := context.Get("request"); exists {
				fmt.Println("经过中间件设置的 request 值为：", value)
				context.JSON(http.StatusOK, gin.H{"request": value})
			} else {
				fmt.Println("经过中间件设置的request值不存在")
				context.JSON(http.StatusOK, gin.H{"request": "经过中间件设置的request值不存在"})
			}
		})
		//使用全局中间件实现全局异常捕获
		middlewareGroup.GET("/globalException/:select", exceptiontest.CreateException)
		// 22. 局部中间件
		middlewareGroup.GET("/localMiddleware", MiddleWare(), func(context *gin.Context) {
			if value, exists := context.Get("request"); exists {
				fmt.Println("request:", value)
				context.JSON(http.StatusOK, gin.H{"request": value})
			} else {
				fmt.Println("request不存在")
				context.JSON(http.StatusOK, gin.H{"request": "经过中间件设置的request值不存在"})
			}
		})
	}

	// 23. 参数验证之结构体验证
	// http://localhost:8088/data/calibration/struct?age=11&name=%E6%9E%AF%E8%97%A4&birthday=2016-01-02
	engine.GET("/data/calibration/struct", paramcalibration.StructCal)

	xormGroup := engine.Group("/xorm")
	{
		// 24. xorm 之插入
		xormGroup.POST("", xorm.InsertOne)
		// 25. xorm 之批量插入（不返回id）
		xormGroup.POST("/batch", xorm.InsertBatchNotReturnId)
		// 26. xorm 之批量插入（返回id）
		xormGroup.POST("/batch/id", xorm.InsertBatchReturnId)
		// 27. xorm 之 插入不同表的各一条记录
		xormGroup.POST("/table", xorm.InsertBatchTable)
		// 28. xorm 之 插入不同表的多条记录
		xormGroup.POST("/table/batch", xorm.InsertBatchTableAndData)
		// 29. xorm 之 查询和更新
		xormGroup.GET("/select/:select", xorm.Select)
		// 30. xorm 之删除
		xormGroup.DELETE("/delete/:select", xorm.Delete)
		// 31. xorm 之简单事务
		xormGroup.GET("/transaction/simple", xorm.SimpleTransaction)
		// 32. xorm 之嵌套事务
		xormGroup.GET("/transaction/nest", xorm.NestTransaction)
		// 33. xorm 之主从数据库读写分离
		xormGroup.GET("/masterAndSlave/:select", xorm.EngineGroup)
		// 34. xorm 之Dump数据库结构和数据
		xormGroup.GET("/dump/:select", xorm.Dump)
		// 35. xorm 之Import 执行数据库SQL脚本
		xormGroup.POST("/import", xorm.Import)
		// 36. xorm 之查询结果集导出csv、tsv、xml、json、xlsx、yaml、html
		xormGroup.GET("/export/:select", xorm.ExportFile)
	}
	return engine
}

func login(c *gin.Context) {
	name := c.DefaultQuery("name", "steven")
	c.String(http.StatusOK, fmt.Sprintf("login %s \n", name))
}

func submit(c *gin.Context) {
	name := c.DefaultQuery("name", "sss")
	c.String(http.StatusOK, fmt.Sprintf("submit %s \n", name))
}

func helloHandler(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"code": 000,
		"data": "routers",
		"msg":  "success"})
}

// MiddleWare 定义局部中间件
func MiddleWare() gin.HandlerFunc {
	return func(context *gin.Context) {
		t := time.Now()
		// TIPS：如果设置了全局中间件，将会先到全局中间件，再到局部中间件。可以控制台日志可以验证
		if value, exists := context.Get("request"); exists {
			fmt.Println("全局中间件设置了request，值为：", value)
		}
		fmt.Println("局部中间件开始执行了 ---> ")
		// 设置变量到Context的key中，可以通过Get()取
		context.Set("request", "局部中间件")
		// 执行函数
		context.Next()
		// 中间件执行完后续的一些事情
		status := context.Writer.Status()
		fmt.Println("局部中间件执行完毕", status)
		t2 := time.Since(t)
		fmt.Println("程序处理用时:", t2)
	}
}
